/* Copyright (c) 2008, John Cronin
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the copyright holder nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */

/* console output functions */

#include "inline.c"
#include "console.h"

static int x = 0;
static int y = 0;

const char hex_digits[] = "0123456789ABCDEF";

unsigned short int * const vmem = (unsigned short int *)0xb8000;

#define DEF_FMT			0x07
#define MAX_X			80
#define MAX_Y			25

void _putc(unsigned char ch);
void _putcf(unsigned char ch, unsigned char fmt);
void _scrup(int lines);
void _updatecursor();

int clear()
{
	int i;
	for(i = 0; i < MAX_X * MAX_Y; i++)
		vmem[i] = (DEF_FMT << 8);
	return 0;
}

int putchar(int c)
{
	_putc((unsigned char)c);
	_updatecursor();
	return 0;
}

int _puts(const char *s, int newline)
{
	while(*s != '\0') {
		_putc((unsigned char)*s);
		s++;
	}
	if(newline != 0)
	{
		x = 0;
		y++;
		if(y >= MAX_Y) {
			_scrup(1);
			y = MAX_Y - 1;
		}
		_updatecursor();
	}
	return 0;
}

int puts(const char *s)
{
	return _puts(s, 1);
}

void _updatecursor()
{
   unsigned short position=(y*MAX_X) + x;

   /* cursor LOW port to vga INDEX register */
   outb(0x3D4, 0x0F);
   outb(0x3D5, (unsigned char)(position&0xFF));
   /* cursor HIGH port to vga INDEX register */
   outb(0x3D4, 0x0E);
   outb(0x3D5, (unsigned char )((position>>8)&0xFF));
}

void _putc(unsigned char ch)
{
	_putcf(ch, DEF_FMT);
}

void _putcf(unsigned char ch, unsigned char fmt)
{
	vmem[x + MAX_X * y] = (fmt << 8) | ch;

	x++;
	if(x >= MAX_X) {
		x = 0;
		y++;
	}
	if(y >= MAX_Y) {
		_scrup(1);
		y = MAX_Y - 1;
	}
}

void _scrup(int lines)
{
	/* scroll up a number of lines */

	/* the number of lines we need to move are MAX_Y - lines */
	/* we start moving from the lines'th line and move that to the first line and so on */
	/* then clear the last lines */

	int i, j;

	for(i = 0; i < MAX_Y; i++) {
		for(j = 0; j < MAX_X; j++) {
			if(i < (MAX_Y - lines))
				vmem[j + MAX_X * i] = vmem[j + MAX_X * (lines + i)];
			else
				vmem[j + MAX_X * i] = (DEF_FMT << 8);
		}
	}
}

int puthex(unsigned long val)
{
	int digit;

	_putc('0');
	_putc('x');

	for(digit = 7; digit >= 0; digit--)
		_putc(hex_digits[(val >> (digit * 4)) & 0xf]);

	_updatecursor();
	return 0;
}
